using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace AssetBundles
{
    public class BuildScript
    {
        public static string overloadedDevelopmentServerURL = "";

        static public string CreateAssetBundleDirectory()
        {
            // Choose the output path according to the build target.
            //string outputPath = Path.Combine(Utility.AssetBundlesOutputPath, Utility.GetPlatformName());

            string outputPath = Application.streamingAssetsPath + "/" + Utility.GetPlatformName();

            if (!Directory.Exists(outputPath))
                Directory.CreateDirectory(outputPath);

            return outputPath;
        }

        public static void BuildAssetBundles()
        {
            BuildAssetBundles(null);
        }

        public static void BuildAssetBundles(AssetBundleBuild[] builds)
        {
            // Choose the output path according to the build target.
            string outputPath = CreateAssetBundleDirectory();
            //AssetBundles\Windows
            //Debug.LogError(outputPath);
            var options = BuildAssetBundleOptions.None;

            bool shouldCheckODR = EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS;
#if UNITY_TVOS
            shouldCheckODR |= EditorUserBuildSettings.activeBuildTarget == BuildTarget.tvOS;
#endif
            if (shouldCheckODR)
            {
#if ENABLE_IOS_ON_DEMAND_RESOURCES
                if (PlayerSettings.iOS.useOnDemandResources)
                    options |= BuildAssetBundleOptions.UncompressedAssetBundle;
#endif
#if ENABLE_IOS_APP_SLICING
                options |= BuildAssetBundleOptions.UncompressedAssetBundle;
#endif
            }

            if (builds == null || builds.Length == 0)
            {
                //@TODO: use append hash... (Make sure pipeline works correctly with it.)
                BuildPipeline.BuildAssetBundles(outputPath, options, EditorUserBuildSettings.activeBuildTarget);
            }
            else
            {
                BuildPipeline.BuildAssetBundles(outputPath, builds, options, EditorUserBuildSettings.activeBuildTarget);
            }
            BuildFileIndex();

            EditorUtility.DisplayProgressBar("导出资源到服务器", "请等待", 0f);
            //更新模式下，将ab资源移动到服务器
            CheckExtractResource();
        }

        /// <summary>
        /// 解压释放资源，并比对更新资源
        /// </summary>
        static void CheckExtractResource()
        {
            //发现服务器路径,资源已经导过一次，先不重新导了，后面按需调整策略
            //string path = MainConfigPath.serverResPath+Utility.GetPlatformName();
            //bool isExists = Directory.Exists(path) && File.Exists(path + "version.txt");
            //MainUtil.Log("isExist:" + isExists + ":" + MainConfigPath.serverResPath);
            //if (isExists)
            //{
            //    return;
            //}
            OnExtractResource();
        }

        /// <summary>
        /// 释放本地资源
        /// </summary>
        /// <returns></returns>
        static void OnExtractResource()
        {
            string dataPath = MainConfigPath.serverResPath; //数据目录,既服务器存储目录路径
            string dataRootPath=dataPath+Utility.GetPlatformName();
            string resPath = MainConfigPath.StreamingAssetsPath_IO(); //游戏包资源目录，既ab资源在工程内的路径

            if (Directory.Exists(dataRootPath)) Directory.Delete(dataRootPath, true);
            Directory.CreateDirectory(dataRootPath);


            //读取工程内版本控制文件的路径
            string infile = resPath + "version.txt";
            //输出到本地平台的版本控制文件的路径
            string outfile = dataPath + "version.txt";
            if (File.Exists(outfile)) File.Delete(outfile);

            MainUtil.Log("正在解包文件:>version.txt");
            MainUtil.Log(infile);
            MainUtil.Log(outfile);
            File.Copy(infile, outfile, true);

            //释放所有文件到数据目录
            string[] files = File.ReadAllLines(outfile);
            int progress = 0;
            foreach (var file in files)
            {
                progress++;
                string[] fs = file.Split('|');
                infile = resPath + fs[0]; 
                outfile = dataPath + fs[0];

                MainUtil.Log("正在解包文件:> " + fs[0]);
                MainUtil.Log("正在解包文件所处源in path:>" + infile);
                MainUtil.Log("正在解包文件所处目标out path:>" + outfile);

                string dir = Path.GetDirectoryName(outfile);
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

                if (File.Exists(outfile))
                {
                    File.Delete(outfile);
                }
                File.Copy(infile, outfile, true);
                MainUtil.Log(outfile.ToString());
                EditorUtility.DisplayProgressBar("export res to server", "Please waiting", progress/file.Length);
            }
            MainUtil.Log("解包完成！！！！！！！");
            ClearStreamingAssets();
            EditorUtility.ClearProgressBar();
        }

        static void ClearStreamingAssets() {
            if (MainConst.NeedClearStreamingAssets) {
                string resPath = MainConfigPath.StreamingAssetsPath_IO(); //游戏包资源目录，既ab资源在工程内的路径
                if (Directory.Exists(resPath))
                {
                    Directory.Delete(resPath, true);
                    Directory.CreateDirectory(resPath);
                }

            }
        }

        //需要生成MD5码进行版本控制的目录和文件
        static List<string> paths = new List<string>();//所有目录的路径集合
        static List<string> files = new List<string>();//所有文件的路径集合
        /// <summary>
        /// 生成文件版本控制
        /// </summary>
        static void BuildFileIndex()
        {
            string resPath = Application.streamingAssetsPath;
            ///----------------------创建文件列表-----------------------
            string newFilePath = resPath + "/version.txt";
            if (File.Exists(newFilePath)) File.Delete(newFilePath);
            paths.Clear();
            files.Clear();

            Recursive(resPath);

            FileStream fs = new FileStream(newFilePath, FileMode.CreateNew);
            StreamWriter sw = new StreamWriter(fs);
            for (int i = 0; i < files.Count; i++)
            {
                string file = files[i];
                string ext = Path.GetExtension(file);
                if (file.EndsWith(".meta") || file.Contains(".DS_Store")) continue;
                //Debug.Log(file);
                //E:/U3dProj/borderExtension/BorderExpansion/borderExpansionProj/Assets/StreamingAssets/StreamingAssets
                //E:/U3dProj/borderExtension/BorderExpansion/borderExpansionProj/Assets/StreamingAssets/StreamingAssets.manifest
                //E:/U3dProj/borderExtension/BorderExpansion/borderExpansionProj/Assets/StreamingAssets/scene/battle
                //E:/U3dProj/borderExtension/BorderExpansion/borderExpansionProj/Assets/StreamingAssets/scene/battle.manifest

                string md5 = MainUtil.md5file(file);
                string value = file.Replace(resPath+"/", string.Empty);
                sw.WriteLine(value + "|" + md5);
            }
            sw.Close(); fs.Close();
        }

        /// <summary>
        /// 遍历目录及其子目录
        /// </summary>
        static void Recursive(string path)
        {
            string[] names = Directory.GetFiles(path);
            string[] dirs = Directory.GetDirectories(path);
            foreach (string filename in names)
            {
                string ext = Path.GetExtension(filename);
                if (ext.Equals(".meta")) continue;
                files.Add(filename.Replace('\\', '/'));
            }
            foreach (string dir in dirs)
            {
                paths.Add(dir.Replace('\\', '/'));
                Recursive(dir);
            }
        }


        public static void WriteServerURL()
        {
            string downloadURL;
            if (string.IsNullOrEmpty(overloadedDevelopmentServerURL) == false)
            {
                downloadURL = overloadedDevelopmentServerURL;
            }
            else
            {
                IPHostEntry host;
                string localIP = "";
                host = Dns.GetHostEntry(Dns.GetHostName());
                foreach (IPAddress ip in host.AddressList)
                {
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                    {
                        localIP = ip.ToString();
                        break;
                    }
                }
                downloadURL = "http://" + localIP + ":7888/";
            }

            string assetBundleManagerResourcesDirectory = "Assets/AssetBundleManager/Resources";
            string assetBundleUrlPath = Path.Combine(assetBundleManagerResourcesDirectory, "AssetBundleServerURL.bytes");
            Directory.CreateDirectory(assetBundleManagerResourcesDirectory);
            File.WriteAllText(assetBundleUrlPath, downloadURL);
            AssetDatabase.Refresh();
        }

        public static void BuildPlayer()
        {
            var outputPath = EditorUtility.SaveFolderPanel("Choose Location of the Built Game", "", "");
            if (outputPath.Length == 0)
                return;

            string[] levels = GetLevelsFromBuildSettings();
            if (levels.Length == 0)
            {
                Debug.Log("Nothing to build.");
                return;
            }

            string targetName = GetBuildTargetName(EditorUserBuildSettings.activeBuildTarget);
            if (targetName == null)
                return;

            // Build and copy AssetBundles.
            BuildScript.BuildAssetBundles();
            WriteServerURL();

#if UNITY_5_4 || UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0
            BuildOptions option = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None;
            BuildPipeline.BuildPlayer(levels, outputPath + targetName, EditorUserBuildSettings.activeBuildTarget, option);
#else
            BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
            buildPlayerOptions.scenes = levels;
            buildPlayerOptions.locationPathName = outputPath + targetName;
            buildPlayerOptions.assetBundleManifestPath = GetAssetBundleManifestFilePath();
            buildPlayerOptions.target = EditorUserBuildSettings.activeBuildTarget;
            buildPlayerOptions.options = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None;
            BuildPipeline.BuildPlayer(buildPlayerOptions);
#endif
        }

        public static void BuildStandalonePlayer()
        {
            var outputPath = EditorUtility.SaveFolderPanel("Choose Location of the Built Game", "", "");
            if (outputPath.Length == 0)
                return;

            string[] levels = GetLevelsFromBuildSettings();
            if (levels.Length == 0)
            {
                Debug.Log("Nothing to build.");
                return;
            }

            string targetName = GetBuildTargetName(EditorUserBuildSettings.activeBuildTarget);
            if (targetName == null)
                return;

            // Build and copy AssetBundles.
            BuildScript.BuildAssetBundles();
            BuildScript.CopyAssetBundlesTo(Path.Combine(Application.streamingAssetsPath, Utility.AssetBundlesOutputPath));
            AssetDatabase.Refresh();

#if UNITY_5_4 || UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0
            BuildOptions option = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None;
            BuildPipeline.BuildPlayer(levels, outputPath + targetName, EditorUserBuildSettings.activeBuildTarget, option);
#else
            BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
            buildPlayerOptions.scenes = levels;
            buildPlayerOptions.locationPathName = outputPath + targetName;
            buildPlayerOptions.assetBundleManifestPath = GetAssetBundleManifestFilePath();
            buildPlayerOptions.target = EditorUserBuildSettings.activeBuildTarget;
            buildPlayerOptions.options = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None;
            BuildPipeline.BuildPlayer(buildPlayerOptions);
#endif
        }

        public static string GetBuildTargetName(BuildTarget target)
        {
            switch (target)
            {
                case BuildTarget.Android:
                    return "/test.apk";
                case BuildTarget.StandaloneWindows:
                case BuildTarget.StandaloneWindows64:
                    return "/test.exe";
                case BuildTarget.StandaloneOSXIntel:
                case BuildTarget.StandaloneOSXIntel64:
                case BuildTarget.StandaloneOSX:
                    return "/test.app";
                // case BuildTarget.WebPlayer:
                // case BuildTarget.WebPlayerStreamed:
                // case BuildTarget.WebGL:
                case BuildTarget.iOS:
                    return "";
                // Add more build targets for your own.
                default:
                    Debug.Log("Target not implemented.");
                    return null;
            }
        }

        static void CopyAssetBundlesTo(string outputPath)
        {
            // Clear streaming assets folder.
            FileUtil.DeleteFileOrDirectory(Application.streamingAssetsPath);
            Directory.CreateDirectory(outputPath);

            string outputFolder = Utility.GetPlatformName();

            // Setup the source folder for assetbundles.
            var source = Path.Combine(Path.Combine(System.Environment.CurrentDirectory, Utility.AssetBundlesOutputPath), outputFolder);
            if (!System.IO.Directory.Exists(source))
                Debug.Log("No assetBundle output folder, try to build the assetBundles first.");

            // Setup the destination folder for assetbundles.
            var destination = System.IO.Path.Combine(outputPath, outputFolder);
            if (System.IO.Directory.Exists(destination))
                FileUtil.DeleteFileOrDirectory(destination);

            FileUtil.CopyFileOrDirectory(source, destination);
        }

        static string[] GetLevelsFromBuildSettings()
        {
            List<string> levels = new List<string>();
            for (int i = 0; i < EditorBuildSettings.scenes.Length; ++i)
            {
                if (EditorBuildSettings.scenes[i].enabled)
                    levels.Add(EditorBuildSettings.scenes[i].path);
            }

            return levels.ToArray();
        }

        static string GetAssetBundleManifestFilePath()
        {
            var relativeAssetBundlesOutputPathForPlatform = Path.Combine(Utility.AssetBundlesOutputPath, Utility.GetPlatformName());
            return Path.Combine(relativeAssetBundlesOutputPathForPlatform,  Utility.GetPlatformName()) + ".manifest";
        }
    }
}
